package com.slipper.system.codeless.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.slipper.common.service.CommonService;
import com.slipper.core.database.IResolver;
import com.slipper.core.database.ResolverFactory;
import com.slipper.datasource.DatasourceUtil;
import com.slipper.system.codeless.bo.DatasourceBO;
import com.slipper.system.codeless.bo.ViewBO;
import com.slipper.system.codeless.dao.ViewDao;
import com.slipper.core.transaction.ReadTransactional;
import com.slipper.core.transaction.WriteTransactional;
import com.slipper.util.DatabaseUtil;
import com.slipper.util.ResultUtil;
import com.slipper.util.Tool;
import com.slipper.util.UUID;
import jakarta.annotation.Resource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@ReadTransactional
public class ViewService {
    @Resource
    private ViewDao viewDao;
    @Resource
    private ViewHistoryService viewHistoryService;
    @Resource
    private DatasourceService datasourceService;
    @Resource
    private SqlSessionFactory sqlSessionFactory;
    @Resource
    private CommonService commonService;
    
    public ViewBO queryBy(String id) {
        return viewDao.queryBy(id);
    }
    
    public List<ViewBO> queryList(ViewBO bo) {
        return viewDao.queryList(bo);
    }

    public List<ViewBO> queryList(String[] ids) {
        return viewDao.queryListByIds(ids);
    }
    
    public PageInfo<ViewBO> queryPage(ViewBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(viewDao.queryList(bo));
    }
    
    @WriteTransactional
    public int save(ViewBO bo) {
        int result;
        if (Tool.isBlank(bo.getCode())) {
            do {
                bo.setCode("v_" + UUID.getUUID());
            } while (existsCode(bo.getId(), bo.getCode()));
        }

        bo.setUpdateTime(System.currentTimeMillis());
        if (Tool.isNotBlank(bo.getId())) {
            bo.setUpdateTime(System.currentTimeMillis());
            result = viewDao.update(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            result = viewDao.insert(bo);
        }
        createOrUpdateView(bo);
        viewHistoryService.save(bo);
        return result;
    }

    @WriteTransactional
    public int saveSimple(ViewBO bo) {
        int result;
        if (Tool.isBlank(bo.getCode())) {
            do {
                bo.setCode("v_" + UUID.getUUID());
            } while (existsCode(bo.getId(), bo.getCode()));
        }

        bo.setUpdateTime(System.currentTimeMillis());
        if (Tool.isNotBlank(bo.getId())) {
            result = viewDao.updateSimple(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            result = viewDao.insert(bo);
        }
        return result;
    }

    private void createOrUpdateView(ViewBO bo) {
        for(String ddl : getResolver().createOrUpdateViewDDL(bo.getCode(), bo.getSqlText())) {
            commonService.executeDDL(ddl);
        }
    }

    @WriteTransactional
    public void saveList(List<ViewBO> scriptList) {
        if (scriptList == null) return;

        scriptList.forEach(script -> {
            if (viewDao.queryBy(script.getId()) != null) {
                viewDao.update(script);
            } else {
                viewDao.insert(script);
            }
        });
    }

    @WriteTransactional
    public int delete(ViewBO bo) {
        if (Tool.isNotBlank(bo.getId())) {
            ViewBO view = viewDao.queryBy(bo.getId());
            if (view != null) {
                if (Tool.isNotBlank(view.getCode())) {
                    commonService.executeDDL(getResolver().dropViewDDL(view.getCode()));
                }
                viewDao.delete(view);
            }
        }
        if (bo.getIds() != null) {
            for (String id : bo.getIds()) {
                ViewBO view = viewDao.queryBy(id);
                if (view != null) {
                    if (Tool.isNotBlank(view.getCode())) {
                        commonService.executeDDL(getResolver().dropViewDDL(view.getCode()));
                    }
                    viewDao.delete(view);
                }
            }
        }
        return 1;
    }

    public long queryTotal(ViewBO bo) {
        PageHelper.startPage(0, 0);
        return new PageInfo<>(viewDao.queryList(bo)).getTotal();
    }

    public ViewBO queryByCode(String code) {
        List<ViewBO> viewList = viewDao.queryByCode(code);
        return viewList.isEmpty() ? null : viewList.getFirst();
    }

    public boolean existsCode(String id, String code) {
        return viewDao.existsCode(id, code) > 0;
    }

    public String parseView(ViewBO bo) {
        Map<String, Object> resultMap = new HashMap<>();
        if (ViewBO.SOURCE_BUILTIN.equals(bo.getSource())) {
            try (SqlSession session = sqlSessionFactory.openSession()) {
                resultMap.put("columnList", DatasourceUtil.getMetaData(session, bo.getSqlText(), bo.getData()));
                resultMap.put("dataList", DatasourceUtil.queryPage(session, bo.getSqlText(), 1, 10, bo.getData()).getList());
            }
        } else {
            DatasourceBO datasource = datasourceService.queryBy(bo.getDatasourceId());
            resultMap.put("columnList", DatasourceUtil.getMetaData(datasource, bo.getSqlText(), bo.getData()));
            resultMap.put("dataList", DatasourceUtil.queryPage(datasource, bo.getSqlText(), 1, 10, bo.getData()).getList());
        }
        return ResultUtil.successResult(resultMap);
    }

    public String queryColumn(ViewBO bo) {
        ViewBO view = viewDao.queryBy(bo.getId());

        Map<String, Object> resultMap = new HashMap<>();
        List<Map<String, Object>> columnList;
        Map<String, String> dataTypeMap;
        if (ViewBO.SOURCE_BUILTIN.equals(view.getSource())) {
            dataTypeMap = ResolverFactory.getResolver(DatabaseUtil.getType()).getDataTypeMap();
            try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
                columnList = DatasourceUtil.getMetaData(sqlSession, view.getSqlText(), bo.getData());
            }
        } else {
            DatasourceBO datasource = datasourceService.queryBy(view.getDatasourceId());
            dataTypeMap = ResolverFactory.getResolver(datasource.getDatabaseType()).getDataTypeMap();
            columnList = DatasourceUtil.getMetaData(datasource.getCode(), view.getSqlText(), bo.getData());
        }

        columnList.forEach(column -> {
            String columnType = (String) column.get("columnTypeName");
            if (Tool.isBlank(columnType)) return;
            column.put("dataType", dataTypeMap.get(columnType));
        });
        resultMap.put("columnList", columnList);
        return ResultUtil.successResult(resultMap);
    }

    public IResolver getResolver() {
        return ResolverFactory.getResolver(DatabaseUtil.getType());
    }
}
